package interfaz2;

import java.net.URL;
import java.util.Iterator;

import org.bouncycastle.openpgp.PGPPublicKey;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

import core.Blog;
import events.AddedPublicKeyEvent;
import events.AddedPublicKeyListener;
import events.AddedSourceEvent;
import events.AddedSourceListener;
import events.DustMouseListener;

/**
 * The view of a Blog following the MVC design pattern. As every new composed
 * component in Dust, it extends DustComposite. There is no much to say about
 * this class, use it wherever you want to show a Blog.
 * 
 * @author ole
 */
public class BlogView extends DustComposite {
	// Class attributes.
	private Blog blog;
	private Label title, owner, channel, via;
	private Table http_sources_tbl, publishers_tbl;
	private int index_inside_comp;
	
	/*
	 * Private classes.
	 */
	
	private class AddedSource2Blog implements AddedSourceListener {
		@Override
		public void handleEvent(AddedSourceEvent event) {
			TableItem titem;
			URL new_url;
			
			new_url = event.getUrl();
			titem = new TableItem(http_sources_tbl, SWT.NONE);
			titem.setText(new_url.toString());
		}
	}
	
	/**
	 * This handler will be called when the user associate a new Publisher
	 * with this Blog in order to refresh the view.
	 */
	private class AddedKey2Blog implements AddedPublicKeyListener {
		@Override
		public void handleEvent(AddedPublicKeyEvent event) {
			PGPPublicKey new_key;
			
			new_key = event.getPublicKey();
			addPublicKey(new_key);
		}
	}
	
	/**
	 * When the option "Delete source" is selected in the menu of a source
	 * (either HTTP or public key), this listener will be called.
	 * 
	 * @author ole
	 */
	private class DeleteSourceListener extends SelectionAdapter {
		// Class attributes.
		private TableItem item;
		
		/**
		 * Class constructor.
		 * 
		 * @param item:
		 * 		The TableItem related to the menu displayed and selected.
		 */
		public DeleteSourceListener(TableItem item) {
			this.item = item;
		}
		
		@Override
		public void widgetSelected(SelectionEvent event) {
			String src;
			Blog blog;
			
			// Remove the source from the Blog and then the TableItem. Since
			// it can be either an URL or a public key but we have no way to
			// discriminate it because in the Table they are just Strings
			// (horrible design, my fault, sorry), we try to remove it from
			// URLs and if it fails then from public keys.
			src = item.getText();
			blog = BlogView.this.blog;
			if (blog.removeUrl(src) || blog.removePublicKey(src))
				item.dispose();
		}
	}
	
	/**
	 * This listener will handle mouse events over Blog keys.
	 * 
	 * @author ole
	 */
	private class KeyMouseListener extends DustMouseListener {
		@Override
		public void mouseUp(MouseEvent event) {
			Table keys_table;
			TableItem item_selected;
			Point point;
			
			// Right clicking over a key will cause to show a menu with
			// the options of that source.
			if (isRightClick(event.button)) {
				if (event.getSource() instanceof Table) {
					keys_table = (Table)event.getSource();
					point = new Point(event.x, event.y);
					item_selected = keys_table.getItem(point);
					if (item_selected != null)
						showSourceMenu(item_selected);
				}
			}
		}
	}
	
	/**
	 * This listener will handle mouse events over blog sources in the sources
	 * table.
	 * 
	 * @author ole
	 */
	private class SourceMouseListener extends DustMouseListener {
		@Override
		public void mouseUp(MouseEvent event) {
			Table sources_table;
			TableItem item_selected;
			Point point;
			
			// Right clicking over a source will cause to show a menu with
			// the options of that source.
			if (isRightClick(event.button)) {
				if (event.getSource() instanceof Table) {
					sources_table = (Table)event.getSource();
					point = new Point(event.x, event.y);
					item_selected = sources_table.getItem(point);
					if (item_selected != null)
						showSourceMenu(item_selected);
				}
			}
		}
	}
	
	/*
	 * Private methods of class BlogView.
	 */
	
	/**
	 * Adds a public key to the table of public keys.
	 * 
	 * @param key:
	 * 		The public key to be represented in the table.
	 */
	@SuppressWarnings("unchecked")
	private void addPublicKey(PGPPublicKey key) {
		TableItem titem;
		String pub_column[];
		Iterator userids_iter;
		StringBuffer str_buf;
		
		titem = new TableItem(this.publishers_tbl, SWT.NONE);
		pub_column = new String[2];
		userids_iter = key.getUserIDs();
		if (userids_iter.hasNext())
			pub_column[0] = (String)userids_iter.next();
		else
			pub_column[0] = "Key without User IDs.";
		
		str_buf = new StringBuffer(key.getFingerprint().length * 2);
		for (byte b: key.getFingerprint())
			str_buf.append(Integer.toString(b, 16).toUpperCase());
		pub_column[1] = str_buf.toString();
			
		titem.setText(pub_column);
	}
	
	/**
	 * Adds a new source to the table of sources.
	 * 
	 * @param url:
	 * 		The new source to be added to the view.
	 */
	private void addSource(URL url) {
		TableItem titem;
		
		titem = new TableItem(this.http_sources_tbl, SWT.NONE);
		titem.setText(url.toString());
	}
	
	/**
	 * This method will be called when someone makes right click over the
	 * source of a Blog. It will show a menu with the different options a
	 * source has.
	 * 
	 * @param source_item:
	 * 		The TableItem inside the Table of sources associated to that
	 * 		source.
	 */
	private void showSourceMenu(TableItem source_item) {
		Menu menu;
		MenuItem menuitem;
		
		// Create the menu then fill it with the possible options.
		menu = new Menu(getShell());
		
		menuitem = new MenuItem(menu, SWT.PUSH);
		menuitem.setText("Delete source");
		menuitem.addSelectionListener(new DeleteSourceListener(source_item));
		
		// Show the menu.
		menu.setVisible(true);
	}
	
	/*
	 * Public methods of class BlogView.
	 */
	
	/**
	 * Class constructor.
	 * 
	 * @param shell:
	 * 		The Shell this Composite will belongs to.
	 * @param blog:
	 * 		The blog of this view.
	 */
	public BlogView(DustComposite comp, Blog blog) {
		super(comp, SWT.NONE);
		this.blog = blog;
		index_inside_comp = comp.getIndexOf(this);
		
		// Check that the index is not -1 (error). In fact this cannot happens
		// never! We have just added this view to the DustComposite so IT IS a
		// child.
		if (index_inside_comp == -1)
			throw new RuntimeException("BlogView is not inside the " +
									   "DustComposite.");
		
		TableColumn column;
		GridData grid;
		Composite tables_comp;
		
		// Create the main composite and configure its layout and a few more
		// features.
		this.setLayout(new GridLayout());
		grid = new GridData(GridData.FILL, GridData.FILL, true, true);
		this.setLayoutData(grid);
		
		// The title.
		this.title = new Label(this, SWT.HORIZONTAL);
		this.title.setText("Title: " + this.blog.getTitle());
		
		// The owner.
		this.owner = new Label(this, SWT.HORIZONTAL);
		this.owner.setText("Owner: " + this.blog.getOwner());
		
		// The channel.
		this.channel = new Label(this, SWT.HORIZONTAL);
		this.channel.setText("Channel: " + this.blog.getChannel());
		
		// The last update via.
		this.via = new Label(this, SWT.HORIZONTAL);
		this.via.setText("Last update via: " + this.blog.getUpdateType());
		
		tables_comp = new Composite(this, SWT.NONE);
		tables_comp.setLayout(new FillLayout(SWT.VERTICAL));
		grid = new GridData(GridData.FILL, GridData.FILL, true, true);
		tables_comp.setLayoutData(grid);
		
		// The table for the HTTP sources.
		this.http_sources_tbl = new Table(tables_comp,
										  SWT.H_SCROLL | SWT.V_SCROLL);
		column = new TableColumn(this.http_sources_tbl, SWT.NONE);
		column.setText("HTTP sources");
		column.pack();
		this.http_sources_tbl.setHeaderVisible(true);
		this.http_sources_tbl.addMouseListener(new SourceMouseListener());
		
		// Add a TableItem to the table for each HTTP source in this Blog
		// containing a String with the URL.
		for (URL http_source: this.blog.getUrls())
			addSource(http_source);
		
		// The table for Publishers.
		this.publishers_tbl = new Table(tables_comp, SWT.H_SCROLL | SWT.V_SCROLL);
		column = new TableColumn(this.publishers_tbl, SWT.NONE);
		column.setText("Public keys User IDs");
		column.pack();
		column = new TableColumn(this.publishers_tbl, SWT.NONE);
		column.setText("Key fingerprint");
		column.pack();
		this.publishers_tbl.setHeaderVisible(true);
		this.publishers_tbl.addMouseListener(new KeyMouseListener());
		
		// Add a TableItem to the table for each Publisher in this blog
		// containing a String with the name and another String with the public
		// key.
		for (PGPPublicKey key: this.blog.getPublicKeys())
			addPublicKey(key);
		
		// Add the handlers for the possible events.
		blog.addAddedPublisherListener(new AddedKey2Blog());
		blog.addAddedSourceListener(new AddedSource2Blog());
	}
	
	/**
	 * Returns the Blog this view is associated to.
	 * 
	 * @return:
	 * 		The Blog this view is associated to.
	 */
	public Blog getBlog() {
		return blog;
	}

	/**
	 * Each BlogView has an index inside the DustComposite it was added at
	 * initialization time. This method returns that index.
	 * 
	 * @return:
	 * 		An integer indicating the index inside the DustComposite of this
	 * 		BlogView.
	 */
	public int getIndex() {
		return index_inside_comp;
	}
}
